/* eslint no-console:off, no-async-promise-executor:off */

import RTM from 'agora-rtm-sdk';
import { simpleRequest } from '@api/http.js';
import { getStorageItem, setStorageItem } from '@utils/StorageManager';

const consoleStyle = 'color:#7fff00;background:#333;line-height:1.5rem;';
const consoleStyleError = 'color:#ff4545;background:#ddd;line-height:1.5rem;';
const consoleStyleWarn = 'color:#FFCC00;background:#000;line-height:1.5rem;';
const consoleStyleInfo = 'color:#76D8E4;background:#000;line-height:1.5rem;';

/**
 * @function getRtmToken
 * @description 获取rtm Token
 * @author: 邓子强
 * @Date: 2022/5/10
 */
const GET_RTM_TOKEN = '/rtm/api/agora/generateToken';
const getRtmToken = (channelName) =>
    simpleRequest(GET_RTM_TOKEN, 'get', { channelName, ignoreRtm: false })();

class AgoraRTM {
    appID = '';

    userId = '';

    rtmToken = '';

    client = null;

    state = 'DISCONNECTED';

    stateChangeCallback = undefined;

    channels = {};

    channelState = undefined;

    channelStateChangeCallback = undefined;

    /**
     * @function constructor
     * @description 构造函数
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {string} userId 当前用户的 displayUserId
     * @param {Object} options 其他选项
     * @param {string} options.appId 声网对应的APP_ID
     * @param {boolean} [options.connect=true] 是否自动执行connect函数
     * @param {function} [options.stateChangeCallback] 状态改变回调函数
     * @return {Promise} 返回一个异步回调结果，携带该类的实例
     */
    constructor(userId, options) {
        return new Promise((resolve) => {
            const {
                appId,
                rtmToken = '',
                connect = true,
                stateChangeCallback = (state, reason) => {
                    console.log('%c RTM状态变化 ', consoleStyleInfo, state);
                    console.log('%c 变化原因:', consoleStyleInfo, reason);
                },
            } = options;
            this.stateChangeCallback = stateChangeCallback;
            console.log('%c RTM 初始化... ', consoleStyle);
            this.userId = userId;
            this.rtmToken = rtmToken;
            this.appID = appId || this.appID;
            connect && this.connect();
            console.log('%c RTM 初始化完成 ', consoleStyle);
            resolve(this);
        });
    }

    // 连接Rtm
    async connect() {
        return new Promise((resolve) => {
            console.log('%c RTM 连接声网服务器... ', consoleStyle);
            this.stateChangeCallback?.('CREATING', '创建RTM实例');
            this.client = RTM.createInstance(this.appID);
            // 显示连接状态变化
            this.client?.on('ConnectionStateChanged', (state, reason) => {
                this.state = state;
                this.stateChangeCallback?.(state, reason);
                console.log(`%c State changed To: ${state} Reason: ${reason}`, consoleStyleInfo);
                if (state === 'ABORTED') {
                    // this.logout();
                }
            });
            console.log('%c RTM 连接完成 ', consoleStyle);
            resolve(this);
        });
    }

    /**
     * @function login
     * @description 登录
     * @author: 邓子强
     * @Date: 2022/5/10
     * @return {Object} 登录成功则返回当前RTM实例，否则抛出异常
     */
    login() {
        return new Promise(async (resolve, reject) => {
            if (!this.client) {
                console.log('%c RTM 登录失败:请先执行connect函数连接RTM ', consoleStyleError);
                reject(new Error('params is error'));
            }
            if (!this.userId) {
                console.log('%c RTM 登录失败 userId:', consoleStyleError, this.userId);
                reject(new Error('userId is not defined'));
            }
            if (['ABORTED ', 'CONNECTED'].includes(this.state)) {
                console.warn('当前实例已经为登录状态，无需重复登录！');
                resolve(this);
            }
            this.rtmToken = await this.getToken();
            if (this.rtmToken) {
                console.log('%c RTM 用户登录 ', consoleStyle);
                this.client
                    ?.login({
                        uid: this.userId,
                        token: this.rtmToken,
                    })
                    .then(() => {
                        console.log('%c RTM 登录完成 ', consoleStyle);
                        resolve(this);
                    })
                    .catch((e) => {
                        reject(e);
                    });
            } else {
                console.log('%c RTM 登录失败: 获取Token失败 ', consoleStyleError);
                reject(new Error('get Token was failed'));
            }
        });
    }

    /**
     * @function logout
     * @description 退出登录
     * @author: 邓子强
     * @Date: 2022/5/10
     * @return {Promise} 是否正确退出
     */
    logout() {
        return new Promise((resolve) => {
            if (this.state !== 'DISCONNECTED') {
                console.log('%c RTM 用户退出 ', consoleStyle);
                this.client
                    ?.logout()
                    .then(() => {
                        if (this.state === 'DISCONNECTED') {
                            console.log('%c RTM 已退出 ', consoleStyle);
                            resolve(true);
                        } else {
                            console.log('%c RTM 退出失败 ', consoleStyleError);
                            resolve(false);
                        }
                    })
                    .catch((e) => {
                        console.log('%c Error: RTM 退出失败 ', consoleStyleError);
                        resolve(false, e);
                    });
            } else {
                console.log('%c RTM 用户未登录 ', consoleStyleWarn);
                resolve(true);
            }
        });
    }

    /**
     * @function receiveMessageFromUser
     * @description 接收消息
     * @author: 邓子强
     * @Date: 2022/5/10
     * @param {function} fn 用来响应接收消息的函数
     * @param {number,string} fn.userId 发送者ID
     * @param {string} fn.messageText 消息内容
     * @param {Object} fn.message 消息内容实体
     */
    receiveMessageFromUser(fn) {
        // 显示对端发送的消息
        this.client?.on('MessageFromPeer', (message, peerId) => {
            console.log('%c RTM 接收到消息 ', consoleStyle);
            console.log('%c 发送者Id: ', consoleStyle, peerId);
            console.log('%c 消息内容: ', consoleStyle, message?.text);
            fn(peerId, message?.text, message);
        });
    }

    /**
     * @function sendMessageToUser
     * @description 发送消息
     * @author: 邓子强
     * @Date: 2022/5/10
     * @param {number,string} receiveUserId 接收者Id
     * @param {string} message 要发送的文本消息
     */
    sendMessageToUser(receiveUserId, message) {
        return new Promise(async (resolve, reject) => {
            if (this.state !== 'CONNECTED') {
                console.log('%c RTM 发送失败，当前用户未登录 ', consoleStyleError, this.state);
                reject(new Error('当前用户未登录'));
            } else {
                // 发送消息
                console.log('%c RTM 发送消息 ', consoleStyle);
                const { hasPeerReceived } = await this.client?.sendMessageToPeer(
                    { text: message },
                    `${receiveUserId}`
                );
                console.log('%c 是否被接收 ', consoleStyle, hasPeerReceived);
                resolve(hasPeerReceived);
            }
        });
    }

    /**
     * @function onChannelCreate
     * @description 创建频道
     * @author: 邓子强
     * @Date: 2022/5/11
     * @requires {string} channelId
     * @param {string} channelId 频道名称(Id) 唯一标识
     * @param {boolean} autoJoin 是否在频道创建完成后自动加入该频道 默认true
     * @return {Object} 返回当前频道实例
     */
    onChannelCreate(channelId, autoJoin = true) {
        return new Promise(async (resolve, reject) => {
            if (channelId && this.client && !this.channels[channelId]) {
                console.log('%c RTM 创建频道 ', consoleStyle);
                const channel = this.client?.createChannel(channelId); // 创建频道
                if (channel) {
                    console.log('%c RTM 创建频道成功 ', consoleStyle, this.channels);
                    this.channels[channelId] = channel;
                    const _state = 'CREATED';
                    this.channels[channelId].channelState = _state;
                    this.channelStateChangeCallback?.(channelId, _state);
                    // console.log('%c RTM 开始监听频道消息 ', consoleStyle);
                    // this.receiveMessageFromChannel(channelId);
                    if (autoJoin) {
                        console.log('%c RTM 自动加入频道：', consoleStyle, channelId);
                        await this.joinChannel(channelId); // 该方法未返回Promise 无法使用await todo
                    }
                    resolve(channel);
                } else {
                    console.log('%c RTM 频道创建失败 ', consoleStyleError);
                    reject(new Error('频道创建失败'));
                }
            } else {
                console.log(
                    '%c RTM 频道创建失败 ',
                    consoleStyleError,
                    '未初始化RTM实例或频道重复创建'
                );
                reject(new Error('未初始化RTM实例或频道重复创建'));
            }
        });
    }

    /**
     * @function joinChannel
     * @description 加入频道
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {string} channelId 频道名称(Id) 唯一标识
     * @param {function} fn 回调函数
     */
    joinChannel(channelId, fn = undefined) {
        if (!this.channels[channelId]) {
            console.log(
                `%c RTM 加入频道失败，频道${channelId}未创建 `,
                consoleStyleWarn,
                this.state
            );
        } else if (this.channels[channelId]?.channelState === 'JOINED') {
            console.log('%c RTM 已加入频道失败，请勿重复加入 ', consoleStyleWarn, this.state);
        } else {
            // 创建并加入频道
            this.channels?.[channelId]
                ?.join()
                .then(() => {
                    const _state = 'JOINED';
                    this.channels[channelId].channelState = _state;
                    this.channelStateChangeCallback?.(channelId, _state);
                    console.log('%c RTM 当前用户加入频道成功，频道ID: ', consoleStyle, channelId);
                    fn?.(channelId, _state);
                })
                .catch((e) => {
                    console.error(e.message, 'joinChannel');
                });
        }
    }

    /**
     * @function levelChannel
     * @description 离开频道
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {string} channelId 频道名称(Id) 唯一标识
     * @param {function} fn 回调函数
     */
    levelChannel(channelId, fn = undefined) {
        return new Promise(async (resolve, reject) => {
            if (!this.channels[channelId]) {
                console.log(
                    '%c RTM 离开频道失败，未进入频道或频道实例不存在 ',
                    consoleStyleError,
                    channelId
                );
                reject(new Error('未进入频道或频道实例不存在'));
            }
            try {
                await this.channels?.[channelId]?.leave();
                const _state = 'LEVEL';
                delete this.channels[channelId];
                this.channelStateChangeCallback?.(channelId, _state);
                console.log('%c RTM 当前用户离开频道成功，频道ID: ', consoleStyle, channelId);
                fn?.(channelId, _state);
                resolve();
            } catch (e) {
                console.error(e.message, 'levelChannel');
                reject(e);
            }
        });
    }

    /**
     * @function receiveMessageFromChannel
     * @description 接收频道消息
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {string} channelId 频道名称(Id) 唯一标识
     * @param {function} fn 用来响应接收消息的函数
     * @param {string} fn.memberId 发送者Id
     */
    receiveMessageFromChannel(channelId, fn = undefined) {
        // // 用户加入频道频道
        this.channels?.[channelId]?.on('MemberJoined', (memberId) => {
            fn?.(memberId, { eventType: 'MemberJoined' });
            console.log('%c RTM 用户加入频道: ', consoleStyle, memberId);
        });
        // 用户离开频道
        this.channels?.[channelId]?.on('MemberLeft', (memberId) => {
            fn?.(memberId, { eventType: 'MemberLeft' });
            console.log('%c RTM 用户离开频道: ', consoleStyle, memberId);
        });
        // 显示对端发送的消息
        this.channels?.[channelId]?.on('ChannelMessage', (message, memberId) => {
            console.log(
                `%c RTM 接收到频道消息 %c 发送者Id: ${memberId}`,
                consoleStyle,
                'color:#F9A03E;background:#333;'
            );
            console.log('%c 消息内容: ', consoleStyle, message?.text);
            fn?.(memberId, { eventType: 'ChannelMessage', messageText: message?.text, message });
        });
    }

    /**
     * @function sendMessageToChannel
     * @description 发送频道消息
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {string} channelId 频道ID
     * @param {string} message 要发送的文本消息
     * @return {Promise} 返回一个Promise来告知发送失败或成功
     */
    sendMessageToChannel(channelId, message) {
        return new Promise((resolve, reject) => {
            if (this.state !== 'CONNECTED') {
                console.log('%c RTM 发送失败，当前用户未登录 ', consoleStyleError, channelId);
                reject(new Error('当前用户未登录'));
            }
            if (!this.channels?.[channelId]) {
                console.log(
                    '%c RTM 发送失败，频道未创建或未加入频道 ',
                    consoleStyleError,
                    channelId
                );
                reject(new Error('频道未创建或未加入频道'));
            }
            // 发送频道消息
            this.channels?.[channelId]
                ?.sendMessage({ text: message })
                .then(() => {
                    console.log(
                        `%c RTM 发送频道消息成功 %c 频道ID: ${channelId}`,
                        consoleStyle,
                        'color:#F9A03E;background:#333;',
                        message
                    );
                    resolve(true);
                })
                .catch((e) => {
                    reject(e);
                });
        });
    }

    /**
     * @function onStateChange
     * @description 状态改变回调函数
     * @author: 邓子强
     * @Date: 2022/5/11
     * @param {function} fn 用来响应状态改变回调的函数
     * @param {string} fn.state 改变后的状态
     * @param {string} fn.reason 触发的原因
     */
    onStateChange(fn) {
        this.stateChangeCallback = fn;
    }

    /**
     * @description 获取RTM Token
     * @author: 邓子强
     * @Date: 2022/7/7
     */
    async getToken() {
        const expire = getStorageItem('RTM_TOKEN_EXPIRE');
        const token = getStorageItem('RTM_TOKEN');
        const KumianmeiToken = getStorageItem('KumianmeiToken');
        if (this.rtmToken) {
            return this.rtmToken;
        }
        if (new Date().getTime() < expire && token) {
            return token;
        }

        if (KumianmeiToken) {
            console.log('%c RTM 获取服务器签发token ', consoleStyle);
            const { rtmToken, rtmTokenExpire } = await getRtmToken();
            if (rtmToken) {
                this.rtmToken = rtmToken;
                setStorageItem('RTM_TOKEN', rtmToken);
                setStorageItem('RTM_TOKEN_EXPIRE', rtmTokenExpire);
                return rtmToken;
            }
            return '';
        }

        console.log('%c 无效的Kumianmei登录用户 ', consoleStyleError);
        return '';
    }
}

export default AgoraRTM;
